package com.zhc.tools.core.fileimport;

import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.annotate.JsonIgnore;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zhc.tools.common.util.BeanUtilsEx;
import com.zhc.tools.common.util.PropertiesHelper;
import com.zhc.tools.common.util.SpringContextHelper;
import com.zhc.tools.core.fileimport.domain.FileStrategySetting;
import com.zhc.tools.core.fileimport.domain.ImportFileResultEnum;
import com.zhc.tools.core.fileimport.domain.ImportLogObject;

import lombok.Data;

/**
 * 导入数据上下文
 * @author zhc
 */
@Data
public class StrategyContext {
	
	private final Log logger = LogFactory.getLog(this.getClass());
	
	/**
	 * 文件名称
	 */
	private String filename;
	
	/**
	 * 导入数据流
	 */
	@JsonIgnore
	private InputStream inputStream;
	
	/**
	 * 导入服务定义对象
	 */
	private FileStrategySetting fileStrategySetting;
	
	/**
	 * 导入批次
	 */
	private String batch;
	
	/**
	 * 导入文件总行数
	 */
	private int totalCount;
	
	/**
	 * 错误数
	 */
	private int errorCount;
	
	/**
	 * 成功数
	 */
	private int successCount;
	
	/**
	 * 返回状态码
	 */
	private int status;
	
	/**
	 * 返回状态信息
	 */
	private String message;
	
	/** 创建人ID */
    private Long createUserId;
    /** 创建人名称 */
    private String createUser;
    
    /**
     * 传入其他参数
     */
    private Map<String, Object> preDataMap;
    
    /**
     * 文件中原数据存储对象fileDataMap
     */
    @JsonIgnore
    private TreeMap<Integer, Map<Integer, Object>> fileDataMap;
    
	/**
	 * 导入的全量数据
	 */
    @JsonIgnore
	private List<ImportLogObject> importLogObjects;
	
	/**
	 * 执行数据
	 */
    @JsonIgnore
	private List<ImportLogObject> executeDataList;
    
    /**
     * 具体处理接口
     */
    @JsonIgnore
	private IDealService iDealService;
	
	/**
	 * 日志记录对象
	 */
	@JsonIgnore
	protected Class<? extends ImportLogObject> baseLogClass;
    
    /**
	 * 是否批量处理，目前只是使用单一处理，暂时不用批量处理
	 */
	private Boolean useMulti = false;
	
	//////////////////////////////////多线程处理信息 /////////////////////////////////////////
    
	/**
	 * 多线程 操作新增导入原数据锁
	 */
	private Lock addImportLogObjectLock = new ReentrantLock();
	
	/**
	 * 多线程 操作新增执行数据锁
	 */
	private Lock addExecuteDataLock = new ReentrantLock();
	
	/**
	 * 多线程设置成功数锁
	 */
	private Lock addSuccessCountLock = new ReentrantLock();
	
	/**
	 * 增加导入数据，主要供多线程使用
	 * @param importLogObject
	 */
	public void addImportLogObject(ImportLogObject importLogObject) {
		addImportLogObjectLock.lock();
		try {
			if (this.importLogObjects == null) {
				this.importLogObjects = Lists.newArrayList();
			}
			this.importLogObjects.add(importLogObject);
			this.totalCount++;
			this.errorCount = this.totalCount;
		} finally {
			addImportLogObjectLock.unlock();
		}
	}
	
	/**
	 * 增加处理数据，主要供多线程处理
	 * @param importLogObject
	 */
	public void addExecuteData(ImportLogObject importLogObject) {
		addExecuteDataLock.lock();
		try {
			if (this.executeDataList == null) {
				this.executeDataList = Lists.newArrayList();
			}
			this.executeDataList.add(importLogObject);
		} finally {
			addExecuteDataLock.unlock();
		}
	}
	
	/**
	 * 记录处理成功数据
	 * @param successCount
	 */
	public void addSuccessCount(int successCount) {
		addSuccessCountLock.lock();
		try {
			this.successCount += successCount;
			this.errorCount = this.totalCount - this.successCount;
		} finally {
			addSuccessCountLock.unlock();
		}
	}
	
	//////////////////////////////////多线程处理信息 /////////////////////////////////////////
	
	
	/**
	 * 开始处理导入数据
	 * @return
	 */
	public boolean doExecute() {
		if (this.fileStrategySetting == null 
					|| StringUtils.isBlank(this.fileStrategySetting.getStrategyServiceType())) {
			throw new RuntimeException("StrategyContext#executeStrategy StrategyServiceType is not be set");
		}
		String strategyServiceName;
		if (StringUtils.isNotBlank(this.fileStrategySetting.getParentStrategyServiceType())) {
			strategyServiceName = this.fileStrategySetting.getParentStrategyServiceType();
		} else {
			strategyServiceName = this.fileStrategySetting.getStrategyServiceType();
		}
		Object strategyService = SpringContextHelper.getBean(strategyServiceName);
		if (strategyService != null && strategyService instanceof IStrategyImportFile) {
			// 初始化处理类
			init();
			return ((IStrategyImportFile) strategyService).execute(this);
		}
		return false;
	}

	private void init() {
		setOperateLogT();
		putAndGetDealService();
	}
	
	/**
	 * 保存传入的其他参数
	 * @param key
	 * @param value
	 */
	public void putPreDataMap(String key, Object value) {
		if (this.preDataMap == null) {
			this.preDataMap = Maps.newHashMap();
		}
		this.preDataMap.put(key, value);
	}
	
	public ImportLogObject createImportLogObject(Entry<Integer, Map<Integer, Object>> entry) 
			throws InstantiationException, IllegalAccessException {
		if (entry != null) {
			ImportLogObject importLogObject = this.getBaseLogClass().newInstance();
			try {
				BeanUtilsEx.copyProperties(importLogObject, this);
				BeanUtilsEx.copyProperties(importLogObject, this.fileStrategySetting);
			} catch (Exception e) {
				this.logger.error(String.format("StrategyContext#createImportLogObject ERROR param【%s】 :", 
						JSON.toJSONString(this)), e);
			}
			importLogObject.setExcelNo(entry.getKey());
			importLogObject.setDataMap(entry.getValue());
			importLogObject.setBody(JSON.toJSONString(entry.getValue()));
			
			this.addImportLogObject(importLogObject);
			return importLogObject;
		}
		return null;
	}
	
	/**
	 * 获取日志对象
	 * @return
	 */
	public Class<? extends ImportLogObject> getBaseLogClass() {
		if (this.baseLogClass == null) {
			throw new RuntimeException("StrategyContext#getBaseLogClass [baseLogService.domainName] is not be class");
		}
		if (!ImportLogObject.class.isAssignableFrom(this.baseLogClass)) {
			throw new RuntimeException("StrategyContext#getBaseLogClass [baseLogService.domainName] is not instanceof Instanceof");
		}
		return baseLogClass;
	}
	
	/**
	 * 设置导入日志对象
	 */
	@SuppressWarnings("unchecked")
	protected void setOperateLogT() {
		try {
			String className = PropertiesHelper.newInstance().getValue("baseLogService.domainName");
			if (StringUtils.isNotBlank(className)) {
				Class<?> clazz = (Class<?>) Class.forName(className);
				
				if (ImportLogObject.class.isAssignableFrom(clazz)) {
					this.baseLogClass = (Class<? extends ImportLogObject>) clazz;
				} else {
					this.logger.error(String.format("StrategyContext#setOperateLogT className【%s】 is not AssignableFrom importLogObject", className));
				}
			}
		} catch (Exception e) {
			this.logger.error("StrategyContext#setOperateLogT prop [baseLogService.domainName] setting ERROR:", e);
		}
		if (this.fileStrategySetting.isSaveLog()) {
			if (this.baseLogClass == null) {
				throw new RuntimeException("StrategyContext#setOperateLogT [baseLogService.domainName] is not be class");
			}
		}
		if (this.baseLogClass == null) {
			this.baseLogClass = ImportLogObject.class;
		}
	}
	
	/**
	 * 设置具体处理实现
	 */
	public void putAndGetDealService() {
		if (this.fileStrategySetting == null) {
			return;
		}
		String dealServiceName = this.fileStrategySetting.getStrategyServiceType();
		
		if (StringUtils.isBlank(dealServiceName)) {
			if (this.useMulti) {
				// 批量执行
				dealServiceName = "dealMultiService";
			} else {
				// 单个执行
				dealServiceName = "dealSingleService";
			}
		}
			
		Object dealService = SpringContextHelper.getBean(dealServiceName);
		if (dealService != null && dealService instanceof IDealService) {
			if (this.useMulti && dealService instanceof IDealMultiService) {
				// 具体批量处理
				this.iDealService = (IDealMultiService) dealService;
			} else if (dealService instanceof IDealSingleService) {
				// 具体单一处理
				this.iDealService = (IDealSingleService) dealService;
			}
		}
	}
	
	/**
	 * 获取具体处理实现
	 * @return
	 */
	public IDealService getiDealService() {
		if (this.iDealService == null) {
			putAndGetDealService();
		}
		if (this.iDealService == null) {
			throw new RuntimeException(String.format("StrategyContext#getiDealService【%s】 is not be set", this.fileStrategySetting.getStrategyServiceType()));
		}
		return this.iDealService;
	}

	/**
	 * 根据枚举类设置code message
	 * @param resultEnum
	 */
	public void setCodeByResultEnum(ImportFileResultEnum resultEnum) {
		this.setCodeByResultEnum(resultEnum, null);
	}
	
	/**
	 * 根据枚举类设置code message
	 * @param resultEnum
	 * @param msg
	 */
	public void setCodeByResultEnum(ImportFileResultEnum resultEnum, String msg) {
		if (resultEnum != null) {
			this.setStatus(resultEnum.getStatus());
			if (StringUtils.isNotBlank(msg)) {
				this.setMessage(msg);
			} else {
				this.setMessage(resultEnum.getMessage());
			}
		}
	}
}